﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using EventFlowLibrary.GameStructure;
using Microsoft.Xna.Framework;
using EventFlowLibrary.Player;
using BEPUphysics;
using Nine;
using EventFlowLibrary.LevelComponents;
using PerformanceUtility.GameDebugTools;
using PerformanceUtility.ShapeRendering;
using Microsoft.Xna.Framework.Media;
using Microsoft.Xna.Framework.Audio;
using ScreenManagement;
using LevelComponents;
using Microsoft.Xna.Framework.Content;

namespace EventFlowLibrary
{
    public class World : DrawableGameComponent, IDisposable
    {
        /// <summary>
        /// The level for this world, containing models and physics.
        /// </summary>
        internal LevelManagement levelManager;

        /// <summary>
        /// The manager for the world's text components.
        /// </summary>
        internal TextManagement textManager;

        /// <summary>
        /// The manager for all game events.
        /// </summary>
        internal GameEventManagement eventManager;

        /// <summary>
        /// Whether we're ending the game.
        /// </summary>
        private bool disposing = false;

        /// <summary>
        /// How long we've played in whole and fractional milliseconds.
        /// </summary>
        public double TimePlayed { get { return timePlayed; } }
        private double timePlayed;

        /// <summary>
        /// Return the character position in the world.
        /// </summary>
        public Vector3 CharacterPosition { get { return levelManager.Character.CharacterController.Body.Position; } }

        /// <summary>
        /// Return the charcter's rotation in the world
        /// </summary>
        public Quaternion CharacterOrientation { get { return levelManager.Character.CharacterController.Body.Orientation; } }

        /// <summary>
        /// Return the rotation of our character, useful for some camera modes.
        /// </summary>
        public float TotalRotationY { get { return levelManager.Character.totalRotationY; } }

        /// <summary>
        /// Return the camera yaw
        /// </summary>
        public float CameraYaw { get { return levelManager.FPSCamera.Yaw; } }

        /// <summary>
        /// Return the camera yaw
        /// </summary>
        public float CameraPitch { get { return levelManager.FPSCamera.Pitch; } }

        /// <summary>
        /// TotalGameTime.TotalMilliseconds when the screen is loaded. 
        /// We need this to subtract from so we get an accurate count when tracking time played.
        /// We cannot reset totalgametime.
        /// </summary>
        private double millisecondsOutOfGameplay;

        /// <summary>
        /// How long we have played the game when loading (0 if starting new).
        /// </summary>
        internal double TimePlayedAtStart { get { return timePlayedAtStart; } }
        private double timePlayedAtStart;

        /// <summary>
        /// Track our screenmanager
        /// </summary>
        internal ScreenManager screenManager;

        ContentManager content;

        /// <summary>
        /// Create the game world with save data.
        /// </summary>
        /// <param name="game"></param>
        /// <param name="_timePlayed"></param>
        /// <param name="_startGameTime">Our total gametime at the start when loading, to get accurate track of time played.</param>
        public World(Game game, double _startGameTime, LoadVariables _loadVariables, ScreenManager _screenManager) : base(game)
        {
            if (_loadVariables == null)
                throw new ArgumentNullException("Load variables cannot be null");

            // Track our variables for loading
            GlobalVariables.loadVariables = _loadVariables;
            screenManager = _screenManager;

            // Track time in game shell (such as screenmanager) until loading this world
            millisecondsOutOfGameplay = _startGameTime;

            // Set our game options
            MediaPlayer.Volume = _screenManager.gameSettings.MusicVolume;
            SoundEffect.MasterVolume = _screenManager.gameSettings.SoundEffectVolume;

            // Check if we are loading a save.
            // If we are loading, we've played for a certain period of time, so track it. timePlayedAtStart is 0 if starting a new game.
            timePlayedAtStart = _loadVariables.timePlayed;

            // Initialize managers
            levelManager = new LevelManagement(game, GlobalVariables.loadVariables, this);
            textManager = new TextManagement(game, this, GlobalVariables.loadVariables.level);
            eventManager = new GameEventManagement(game, GlobalVariables.loadVariables.level, this);

            // Create the text manager and set draw order late to draw on top of everything.
            textManager.DrawOrder = 999;
            this.Game.Components.Add(textManager);

            // Create the level
            levelManager.DrawOrder = 898;
            levelManager.Character.Camera.Inverted = (_screenManager.gameSettings.Inverted ? 1 : -1);
            levelManager.Character.Camera.CameraBobEnabled = (_screenManager.gameSettings.CameraBobEnabled ? true : false);
            this.Game.Components.Add(levelManager);

            // Create the event manager
            eventManager.DrawOrder = 899;
            this.Game.Components.Add(eventManager);
        }

        public override void Initialize()
        {
            base.Initialize();

//#if TRACE
//            DebugShapeRenderer.Initialize(this.Game.GraphicsDevice);
//            DebugSystem.Instance.EventDisplayer.Enabled = false;
//            DebugSystem.Instance.EventDisplayer.Visible = false;
//            DebugSystem.Instance.FpsCounter.Enabled = false;
//            DebugSystem.Instance.FpsCounter.Visible = false;
//            DebugSystem.Instance.TimeRuler.Enabled = false;
//            DebugSystem.Instance.TimeRuler.Visible = false;
//            DebugSystem.Instance.TimeRuler.ShowLog = false;
//#endif

            // Assign our starting game states
            eventManager.RefreshGameStates();
        }

        protected override void LoadContent()
        {
            // Create the content manager
            ContentManager content = new ContentManager(this.Game.Content.ServiceProvider, "Content");

            base.LoadContent();

            // Have to draw seperately so that pause draws OVER the game play
            this.Game.Components.Remove(levelManager);
            this.Game.Components.Remove(textManager);
            this.Game.Components.Remove(eventManager);
        }

        public override void Draw(GameTime gameTime)
        {
            levelManager.Draw(gameTime);
            eventManager.Draw(gameTime);
            textManager.Draw(gameTime);

            base.Draw(gameTime);

        }

        public override void Update(GameTime gameTime)
        {
            // Track our time played
            timePlayed = gameTime.TotalGameTime.TotalMilliseconds + timePlayedAtStart - millisecondsOutOfGameplay;

            // Draw our level
            levelManager.Update(gameTime);
            eventManager.Update(gameTime);
            textManager.Update(gameTime);

            base.Update(gameTime);
        }

        public void HandleInput(GameTime _gameTime, InputState _inputState)
        {
            // Handle input for text manager
            textManager.HandleInput(_gameTime, _inputState);

            // Handle input for level
            levelManager.HandleInput(_gameTime, _inputState);

            // Handle input for events
            eventManager.HandleInput(_gameTime, _inputState);
        }

        protected override void UnloadContent()
        {
            base.UnloadContent();

            this.Game.Components.Remove(levelManager);
            this.Game.Components.Remove(textManager);
            this.Game.Components.Remove(eventManager);

            //levelManager = null;
            //textManager = null;
            //eventManager = null;
        }

        protected override void Dispose(bool disposing)
        {
            this.disposing = true;

            this.Game.Components.Add(levelManager);
            this.Game.Components.Add(textManager);
            this.Game.Components.Add(eventManager);

            //levelManager.Dispose();
            //textManager.Dispose();
            //eventManager.Dispose();

            base.Dispose(disposing);
        }
    }
}
